/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/08/07
*/
#include "Surface.h"
#include "../edui_sdl.h"

EDUI_NAMESPACE_BEGIN

#ifdef EDUI_TEST_NEW_DELETE
int Surface::m_newdeleteNum = 0;
int Surface::_GetNewDeleteNum()
{
    return m_newdeleteNum;
}
#endif

Surface::Surface() : m_frames(0), m_num(0)
{}

Surface::~Surface()
{
    this->Destroy();
}

Surface* Surface::ToSurface(ISurface* surface)
{
    return dynamic_cast<Surface*>(surface);
}

Surface* Surface::GetInterface()
{
    Surface* surface = new Surface;
#ifdef EDUI_TEST_NEW_DELETE
    ++m_newdeleteNum;
    Log("[Surface] new surface : %d", surface);
#endif
#ifdef EDUI_USE_LUAPLUS
    LuaMgrPtr->Add("Surface", surface);
#endif
    return surface;
}

void Surface::Release()
{
#ifdef EDUI_TEST_NEW_DELETE
    --m_newdeleteNum;
    Log("[Surface] delete surface : %d", this);
#endif
#ifdef EDUI_USE_LUAPLUS
    LuaMgrPtr->Remove(this);
#endif
    delete this;
}

void Surface::_Add(SDL_Surface* surface)
{
    m_frames = new AG_Frame[1];
    m_frames[0].surface = surface;
    m_frames[0].x = 0;
    m_frames[0].y = 0;
    m_frames[0].disposal = 0;
    m_frames[0].delay = 0;
    m_frames[0].user = 0;
    m_num = 1;
}

bool Surface::Create(const char* file, const char* name, int id)
{
    if (this->IsOK()) return false;
    if (!file) return false;
    SurfaceMgr* mgr = IEnginePtr->GetSurfaceMgr();

    SDL_RWops *rwop = SDL_RWFromFile(file, "rb");
    if (IMG_isGIF(rwop)){
        m_num = AG_LoadGIF_RW(rwop, 0, 0);
        m_frames = new AG_Frame[m_num];
        if (AG_LoadGIF_RW(rwop, m_frames, m_num) == 0){
            this->Destroy();
            return false;
        }
    } else {
        SDL_Surface *surface = IMG_Load(file);
        if (!surface) return false;
        this->_Add(surface);
    }
    SDL_RWclose(rwop);
    
    if (!name){
        if (!mgr->Add(file, this, id)){
            this->Destroy();
            return false;
        }
    } else {
        if (!mgr->Add(name, this, id)){
            this->Destroy();
            return false;
        }
    }
    return true;
}

bool Surface::Create(void* mem, int size, const char* name, int id, const char* type)
{
    if (this->IsOK()) return false;
    if (!name || !mem || size <= 0) return false;
    SurfaceMgr* mgr = IEnginePtr->GetSurfaceMgr();

    SDL_RWops *rwop = SDL_RWFromMem(mem, size);
    if (IMG_isGIF(rwop)){
        m_num = AG_LoadGIF_RW(rwop, 0, 0);
        m_frames = new AG_Frame[m_num];
        if (AG_LoadGIF_RW(rwop, m_frames, m_num) == 0){
            SDL_RWclose(rwop);
            this->Destroy();
            return false;
        }
    } else {
        SDL_Surface *surface = 0;

        if (!type) surface = IMG_Load_RW(rwop, 0);
        else {
            String tmp = type;
            tmp.MakeUpper();        
            surface = IMG_LoadTyped_RW(rwop, 0, tmp.GetString());
        }
        
        if (!surface){
            SDL_RWclose(rwop);
            return false;
        }
        this->_Add(surface);
    }
    SDL_RWclose(rwop);
    
    if (!mgr->Add(name, this, id)){
        this->Destroy();
        return false;
    }
        
    return true;
}

bool Surface::CreateRGB(int width, int height, int depth,
            Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask, const char* name, int id)
{
    if (this->IsOK()) return false;
    if (!name) return false;
    SDL_Surface *surface = SDL_CreateRGBSurface(0, width, height, depth,
                                    Rmask, Gmask, Bmask, Amask);
    if (!surface) return false;
    this->_Add(surface);

    SurfaceMgr* mgr = IEnginePtr->GetSurfaceMgr();
    if (!mgr->Add(name, this, id)){
        this->Destroy();
        return false;
    }
    return true;
}

bool Surface::CreateRGBFrom(void *pixels,
                int width, int height, int depth, int pitch,
                Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
                Uint32 Amask, const char* name, int id)
{
    if (this->IsOK()) return false;
    if (!name) return false;
    SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(pixels,
                width, height, depth, pitch,
                Rmask, Gmask, Bmask, Amask);
    if (!surface) return false;
    this->_Add(surface);
    
    SurfaceMgr* mgr = IEnginePtr->GetSurfaceMgr();
    if (!mgr->Add(name, this, id)){
        this->Destroy();
        return false;
    }
    return true;
}

// create use this function, you must Destroy and Release this surface.
bool Surface::Create(SDL_Surface* surface)
{
    if (this->IsOK()) return false;
    if (!surface) return false;
    this->_Add(surface);
    
    return true;
}

bool Surface::IsOK()
{
    if (!m_frames) return false;
    return true;
}

void Surface::Destroy()
{
    if (m_frames){
        AG_FreeSurfaces(m_frames, m_num);
        delete m_frames;
        m_frames = 0;
        m_num = 0;
    }
}

//surface number.
int Surface::GetNum()
{
    return m_num;
}

Point Surface::GetOffset(int id)
{
    Point pt;
    if (id >= 0 && id < m_num){
        pt.Reset(m_frames[id].x, m_frames[id].y);
    }
    return pt;
}

int Surface::GetDelay(int id)
{
    if (id < 0 || id >= m_num) return 0;
    return m_frames[id].delay;
}

bool Surface::SetBlendMode(BlendMode mode, int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_SetSurfaceBlendMode(m_frames[id].surface, (SDL_BlendMode)mode);
}

BlendMode Surface::GetBlendMode(int id)
{
    SDL_BlendMode mode = SDL_BLENDMODE_NONE;
    if (id >= 0 && id < m_num){
        SDL_GetSurfaceBlendMode(m_frames[id].surface, &mode);
    } else {
        Log("[EDUI] Surface::GetBlendMode : error surface id.");
    }
    return (BlendMode)mode;
}

bool Surface::SetColorMod(Uint8 r, Uint8 g, Uint8 b, int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_SetSurfaceColorMod(m_frames[id].surface, r, g, b);
}

Color Surface::GetColorMod(int id)
{
    Color col;
    if (id >= 0 && id < m_num){
        SDL_GetSurfaceColorMod(m_frames[id].surface, &col.r, &col.g, &col.b);
    }
    return col;
}

bool Surface::SetAlphaMod(Uint8 alpha, int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_SetSurfaceAlphaMod(m_frames[id].surface, alpha);
}

Uint8 Surface::GetAlphaMod(int id)
{
    Uint8 alpha;
    if (id >= 0 && id < m_num){
        SDL_GetSurfaceAlphaMod(m_frames[id].surface, &alpha);
    }
    return alpha;
}

bool Surface::SetClip(const Rect *rect, int id)
{
    if (id < 0 || id >= m_num) return false;
    if (!rect) return false;
    SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
    return !SDL_SetClipRect(m_frames[id].surface, &rc);
}

Rect Surface::GetClip(int id)
{
    SDL_Rect rc = {0};
    if (id >= 0 && id < m_num){
        SDL_GetClipRect(m_frames[id].surface, &rc);
    }
    return Rect(rc.x, rc.y, rc.w, rc.h);
}

bool Surface::Lock(int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_LockSurface(m_frames[id].surface);
}

void Surface::Unlock(int id)
{
    if (id < 0 || id >= m_num) return;
    SDL_UnlockSurface(m_frames[id].surface);
}

Size Surface::GetSize(int id)
{
    if (id < 0 || id >= m_num) return Size(0, 0);
    if (!m_frames[id].surface) return Size(0, 0);
    return Size(m_frames[id].surface->w, m_frames[id].surface->h);
}

int Surface::Pitch(int id)
{
    if (id < 0 || id >= m_num) return 0;
    if (!m_frames[id].surface) return 0;
    return m_frames[id].surface->pitch;
}

//read/write
void* Surface::Pixels(int id)
{
    if (id < 0 || id >= m_num) return 0;
    if (!m_frames[id].surface) return 0;
    return m_frames[id].surface->pixels;
}
//read/write
void* Surface::UserData(int id)
{
    if (id < 0 || id >= m_num) return 0;
    if (!m_frames[id].surface) return 0;
    return m_frames[id].surface->userdata;
}

SDL_Surface* Surface::GetSDLSurface(int id) const
{
    if (id < 0 || id >= m_num) return 0;
    return m_frames[id].surface;
}

EDUI_NAMESPACE_END
